#include "..\Clients\Clients.h"
#include "..\Properties\Properties.h"
#include "..\Utils\Utils.h"
#include "..\Matching\Matching.h"
#include <conio.h>

#pragma warning (disable: 4996)
#pragma warning (disable: 4090)

//Call to free memory of clients and properties.
void FreeFiles(Client *ClientsArray,int *ClientCount,List PropertiesList,int *PropertiesCount)
{
  FreeClientAllocationsBeforeExit(ClientsArray,ClientCount);
  FreePropertiesAllocationsBeforeExit(PropertiesList,PropertiesCount);
}
//Write files of Clients and Properties.
void WriteFiles(Client *ClientsArray,int *ClientCount,List PropertiesList,int *PropertiesCount)
{
   WriteClientsToFile(ClientsArray,ClientCount);
   WritePropertiesToFile(PropertiesList,PropertiesCount);	  
   ConfirmationMessage("Files Saved Successfuly.");
}
//Shows main match main menu. 
void Show_Matching_Menu(Client *ClientsArray,int *ClientCount,List PropertiesList,int *PropertiesCount)
{
int mid;

if (*PropertiesCount==0)
{
	ConfirmationMessage("There are no properties in the system");
	return;
}
mid = menu(MatchingStrings,MathingStringsLength,0,"Select Match",12);
if (mid==-1)
	return;

switch (mid){
case 0:Match_Client(ClientsArray,ClientCount,PropertiesList,PropertiesCount);break;
case 1:Match_Property(ClientsArray,ClientCount,PropertiesList,PropertiesCount);break;
case 2:Match_ClientsProperties(ClientsArray,ClientCount,PropertiesList,PropertiesCount);break;
}

}

//Main comparison counter. on each must field inc the must, on addition inc the addition.
void CompareDemandProperty(DemandProperty *D,DemandProperty *P,int *must,int *addition)
{
	*must = 0;
	*addition = 0;
	// if not active did't count
	if (P->PropertyRecord.PropertyActive==1)
		return;

	if (strcmp(D->DemandRecord.DemandCity,P->PropertyRecord.PropertyCity)==0)
		(*must)++;
	if (D->DemandRecord.DemandKind==P->PropertyRecord.PropertyKind)
		(*must)++;
	if(D->DemandRecord.DemandPrice==P->PropertyRecord.PropertyPrice)
		(*must)++;
	if(D->DemandRecord.DemandRooms==P->PropertyRecord.PropertyRooms)
		(*addition)++;
	if(D->DemandRecord.DemandType==P->PropertyRecord.PropertyType)
		(*addition)++;

}

//Creates using additional record of all match variations of the properties
//and clients, then calc the matching results, sort the results,
//and show them to the user using interactive main menu.
void Match_ClientsProperties(Client *ClientsArray,int *ClientCount,List PropertiesList,int *PropertiesCount)
{
Match_Result *Matches=NULL;
int i,count=0,pid;
Position PP,PD;
DemandProperty *DP,*DD;

if (*ClientCount==0)
{
	ConfirmationMessage("There are no users in the system.");
	return;
}
if (*PropertiesCount==0)
{
	ConfirmationMessage("There are no properties in the system.");
	return;
}

for (i=0;i<*ClientCount;i++)
{
	if (ClientsArray[i].RequestCount>0)
	{
		PD = First(ClientsArray[i].RequestList);
		Matches = (Match_Result*)realloc(Matches,sizeof(Match_Result)*(ClientsArray[i].RequestCount*(*PropertiesCount) + count));
		while (PD)
		{
			DD = RetrieveAdr(PD);
			PP = First(PropertiesList);
			pid=0;
			while (PP)
			{
				DP = RetrieveAdr(PP);

				Matches[count].D = DD; //save the pointer to demand item
				Matches[count].P = DP; //save the pointer propertu item
				Matches[count].ClientPointer = &(ClientsArray[i]);
				Matches[count].PropertyIndex = pid+1;
				CompareDemandProperty(DD,DP,&(Matches[count].must),&(Matches[count].addition));

				PP = Advance(PP);
				pid++;
				count++;
			}

			PD = Advance(PD);
		}

	}

}
//sort all the results
qsort(Matches,count,sizeof(Match_Result),Sort_Match_Client);
//show to the user
Print_Matches(Matches,count);
//free all
free(Matches);

}

// make the same as previous but for one property
void Match_Property(Client *ClientsArray,int *ClientCount,List PropertiesList,int *PropertiesCount)
{
Match_Result *Matches=NULL;
int pid,count=0,i;
DemandProperty *DP,*DD;
Position PP,PD;

pid = SelectPropertyFromUser(PropertiesList,PropertiesCount);
if (pid==-1)
	return;

if (*ClientCount==0)
{
	ConfirmationMessage("There are no users in the system.");
	return;
}

PP = First(PropertiesList);
PP = AdvanceEx(PP,pid);
DP = RetrieveAdr(PP);

for(i=0;i<*ClientCount;i++)
{
	if (ClientsArray[i].RequestCount>0)
	{
		Matches = (Match_Result*)realloc(Matches,sizeof(Match_Result)*(ClientsArray[i].RequestCount + count));
		PD = First(ClientsArray[i].RequestList);
		while (PD)
		{
			DD = RetrieveAdr(PD);
			Matches[count].D = DD;
			Matches[count].P = DP;
			Matches[count].ClientPointer = &(ClientsArray[i]);
			Matches[count].PropertyIndex = pid+1;
			CompareDemandProperty(DD,DP,&(Matches[count].must),&(Matches[count].addition));
			
			PD = Advance(PD);
			count++;
		}
	}
	
}
qsort(Matches,count,sizeof(Match_Result),Sort_Match_Client);

Print_Matches(Matches,count);

free(Matches);

}
// make the same as previous but for one client only.
void Match_Client(Client *ClientsArray,int *ClientCount,List PropertiesList,int *PropertiesCount)
{
int clientid,demands_count,properties_count,i,j;
Match_Result *Matches;
Position PD,PP;
DemandProperty *DD,*DP;

if (*ClientCount==0)
{
	ConfirmationMessage("There are no users in the system !");
	return;
}

clientid = SelectClientFromList(ClientsArray,ClientCount);
if(clientid==-1)
	return;

if (ClientsArray[clientid].RequestCount==0)
{
	ConfirmationMessage("Client have no Demands");
	return;
}

demands_count = ClientsArray[clientid].RequestCount;
properties_count = *PropertiesCount;
Matches = (Match_Result*)malloc(sizeof(Match_Result)*(demands_count*properties_count));

PP = First(PropertiesList);
for(i=0;i<*PropertiesCount;i++)
{	
	DP = RetrieveAdr(PP);
	PD = First(ClientsArray[clientid].RequestList);
	for(j=0;j<demands_count;j++)
	{
		DD = RetrieveAdr(PD);
		Matches[i*demands_count + j].D = DD;
		Matches[i*demands_count + j].P = DP;
		Matches[i*demands_count + j].PropertyIndex = i+1;
		Matches[i*demands_count + j].ClientPointer =  &(ClientsArray[clientid]);

		CompareDemandProperty(DD,DP,&(Matches[i*demands_count + j].must),&(Matches[i*demands_count + j].addition));

		PD = Advance(PD);
	}
	PP = Advance(PP);

}

qsort(Matches,demands_count*properties_count,sizeof(Match_Result),Sort_Match_Client);

Print_Matches(Matches,demands_count*properties_count);

free(Matches);
}

// sort the results, must is the main, addition is the second.
int Sort_Match_Client(const void *arg1, const void *arg2)
{
	Match_Result MR1,MR2;
	
	MR1 = (*(Match_Result *)arg1); 
	MR2 = (*(Match_Result *)arg2); 
	
	if (MR1.must < MR2.must)
		return 1;
	else if(MR1.must > MR2.must)
		return -1;
	else if(MR1.addition<MR2.addition)
		return 1;
	else if (MR1.addition>MR2.addition)
		return -1;
	else
		return 0;
	
}

//update string of current page.
void UpdateMatchPages(int pageindex,int totalpages,char *BUF)
{
sprintf(BUF,"Page [%d] of Total [%d].",pageindex,totalpages);
}

// Print all the matches using interactive menu, that user can select, 
// and then to see the real diferences.
void Print_Matches(Match_Result *M,int size)
{
int i=0,count=0,results=0,pages=0,currentpage=0;
char *BUF,SMatches[20];
int MatchIndex[MaxResults];

BUF = (char *)malloc(sizeof(char)*100);

showoutputimage(0);

// count all the results.
for(i=0;i<size;i++)
	if (M[i].must>=MustMatches)
		results++;

puttext("[Enter-Space],[Up-Down]",54,1,0x0d,0,0);

sprintf(SMatches,"Total {%d} Matches",results);
puttext(SMatches,40,1,0x0a,1,0);
fillascii(3,2,76,2,'-',0x0a);

pages = results / MaxResults; //count pages

UpdateMatchPages(currentpage+1,pages+1,BUF);
puttext(BUF,3,1,0x0d,0,0);

for(i=0;i<size;i++)
{
	// if this result index is valid, update and create info to show.
	if (M[i].must>=MustMatches)
	{
		MatchIndex[count] = i;
		M[i].ValidMatchIndex = currentpage*MaxResults + count+1;
		fillascii(3,3+count*4,76,3+count*4,(char)LineX,0x02);

		sprintf(BUF,"%d",currentpage*MaxResults+count+1);
		PutFontNumber(72-7*(currentpage*MaxResults+count+1>=10),3 + count*4 + 1,BUF,0);

		puttext("Demand Info: ",4,3+4*count+1,0x0f,0,0);
		puttext("Property Info: ",4,3+4*count+2,0x0f,0,0);
		puttext("Match Info: ",4,3+4*count+3,0x08,0,0);

		FillDemandInfo(M[i].D,&BUF);
		puttext(BUF,20,3+count*4 + 1,0x07,0,0);
		
		FillPropertyInfo(M[i].P,&BUF,0,0);
		puttext(BUF,20,3+count*4 + 2,0x07,0,0);
			
		FillMatchInfo(&(M[i]),&BUF);
		puttext(BUF,20,3+count*4 + 3,0x08,0,0);

		count++;

		if (count>=MaxResults) //if we see more than X results, stop, and select
		{

			fillascii(3,3+count*4,76,3+count*4,(char)LineX,0x02);
			SelectMatchMenu(M,MatchIndex,currentpage,count);
			//------- Re Initilize counters and text to the next time -----
			count=0;
			currentpage++;
			
			showoutputimage(0);
			puttext("[Enter-Space],[Up-Down]",54,1,0x0d,0,0);
			puttext(SMatches,40,1,0x0a,1,0);
			fillascii(3,2,76,2,'-',0x0a);


			UpdateMatchPages(currentpage+1,pages+1,BUF);
			puttext(BUF,3,1,0x0d,0,0);

		}
	
	}
	fillascii(3,3+count*4,76,3+count*4,(char)LineX,0x02);

}

if (count==0)
	ConfirmationMessage("Matches Not Found");
else
	SelectMatchMenu(M,MatchIndex,pages,count); // showing the final results.
	
free(BUF);
}

// Navigate throught the results in the result page, and select one, 
// using space, to see the all result info.
int SelectMatchMenu(Match_Result *MR,int *mind,int pageindex,int count)
{
char ch;
int ind=0,j=0;
do
{
for(j=0;j<=count;j++)
	fillascii(3,3+j*4,76,3+j*4,(char)LineX,0x02);
for(j=0;j<count;j++)
	if (j==ind)
	{
		fillascii(3,3+j*4,76,3+j*4,(char)LineX,0x1a);
		fillascii(3,3+(j+1)*4,76,3+(j+1)*4,(char)LineX,0x1a);
	}

ch = getch();
	if (ch==0,ch==80)
		ind++;
	if (ch==0,ch==72)
		ind--;
	
	if (ch==32) //space pressed, showing additional result info.
		ShowMatchDetails(&(MR[mind[ind]]));
	
	if (ind>count-1)
		ind=0;
	if(ind<0)
		ind = count-1;

}
while (ch!=13 && ch!=27);

if (ch==27 || ch==13)
	return-1;
else
	return ind;
}

// fill line of the result info.
void FillMatchInfo(Match_Result *MR,char **BUF)
{
sprintf(*BUF,"Client Name: %s, Property Index [%d], (%.2f%s).",
MR->ClientPointer->Name,MR->PropertyIndex,((MR->must + MR->addition)/MaxMatches)*100,"%%");
}

//Showing the additional information about result that is selected from the
// main menu of the results page.
void ShowMatchDetails(Match_Result *MR)
{
	CHAR_INFO *CI;
	char Num_S[4];
	int x0=0,x1=0;
	int clr;
	CI = PushScreen();
	showoutputimage(1);
	
	sprintf(Num_S,"%d",(int)(((MR->must + MR->addition)/MaxMatches)*100));
	x0 = 40 - ((int)(strlen(Num_S)+1)*6)/2;
	settextcolor(0x03);
	x1=x0-2 + ((int)(strlen(Num_S)+1)*6) + 5;
	box(x0-2,8,x1,14); 
	PutFontNumber(x0,10,Num_S,1);
	fillascii(40,1,40,7,(char)LineY,0x03);
	fillascii(40,15,40,23,(char)LineY,0x03);

	puttext("Demand",20,2,0x0b,1,0);
	puttext("Property",60,2,0x0b,1,0);
	fillascii(3,3,39,3,'-',0x0b);
	fillascii(41,3,76,3,'-',0x0b);

	puttext("Client Name: ",5,5,0x0f,0,0);
	puttext(MR->ClientPointer->Name,5+13,5,0x07,0,0);

	puttext("City: ",5,7,0x0f,0,0);
	puttext("City: ",5+40,7,0x0f,0,0);

	clr = (!strcmp(MR->D->DemandRecord.DemandCity,
				   MR->P->PropertyRecord.PropertyCity))?(0x1f):(0x07);
	
	puttext(MR->D->DemandRecord.DemandCity,5+6,7,clr,0,0);
	puttext(MR->P->PropertyRecord.PropertyCity,40 + 5 + 6,7,clr,0,0);
	
	puttext("Kind: ",5,9,0x0f,0,0);
	puttext("Kind: ",x1+2,9,0x0f,0,0);
	
	clr = ( MR->D->DemandRecord.DemandKind== MR->P->PropertyRecord.PropertyKind)
			?(0x1f):(0x4f);
	
	puttext(DPKindStrings[MR->D->DemandRecord.DemandKind],5+6,9,clr,0,0);
	puttext(DPKindStrings[MR->P->PropertyRecord.PropertyKind],x1+ 2 + 6,9,clr,0,0);

	puttext("Type: ",5,11,0x0f,0,0);
	puttext("Type: ",x1+2,11,0x0f,0,0);

	clr = ( MR->D->DemandRecord.DemandType==MR->P->PropertyRecord.PropertyType)
			?(0x1f):(0x4f);
	
	puttext(DPDemandTypeStrings[MR->D->DemandRecord.DemandType],5+6,11,clr,0,0);
	puttext(DPPropertyTypeStrings[MR->P->PropertyRecord.PropertyType],x1+ 2 + 6,11,clr,0,0);

	puttext("Rooms: ",5,13,0x0f,0,0);
	puttext("Rooms: ",x1+2,13,0x0f,0,0);

	clr = (MR->D->DemandRecord.DemandRooms==MR->P->PropertyRecord.PropertyRooms)
			?(0x1f):(0x4f);
	
	putnumber(MR->D->DemandRecord.DemandRooms,5+6,13,clr,0,0,NULL,NULL);
	putnumber(MR->P->PropertyRecord.PropertyRooms,x1 + 2 + 7,13,clr,0,0,NULL,NULL);

	puttext("Price: ",5,15,0x0f,0,0);
	puttext("Price: ",40+2,15,0x0f,0,0);

	clr = (MR->D->DemandRecord.DemandPrice==MR->P->PropertyRecord.PropertyPrice)
			?(0x1f):(0x4f);
	putnumber(MR->D->DemandRecord.DemandPrice,5+6,15,clr,0,0,NULL,"$");
	putnumber(MR->P->PropertyRecord.PropertyPrice,40 + 2 + 7,15,clr,0,0,NULL,"$");

	puttext("Contact 1: ",40+2,17,0x0f,0,0);
	puttext(MR->P->PropertyRecord.Contacts[0].FullName,2+40 +11,17,0x07,0,0);

	puttext("Phone ",40+2,18,0x0f,0,0);
	puttext(MR->P->PropertyRecord.Contacts[0].Phone,2+40 +7,18,0x07,0,0);

	puttext("Contact 2: ",40+2,19,0x0f,0,0);
	puttext(MR->P->PropertyRecord.Contacts[1].FullName,2+40 +11,19,0x07,0,0);

	puttext("Phone ",40+2,20,0x0f,0,0);
	puttext(MR->P->PropertyRecord.Contacts[1].Phone,2+40 +7,20,0x07,0,0);

	sprintf(Num_S,"%d",MR->ValidMatchIndex);
	puttext("Match Index: ",3,23,0x0f,0,0);
	puttext(Num_S,3+13,23,0x2f,0,0);
	
	puttext("Press Any Key To Continiue...",40,23,0x3f,1,0);

	getch();
	PopScreen(CI);

}